//
//  APIService.swift
//  Loop
//
//  Created by thomas on 2020/9/23.
//

import Foundation

enum HttpRequestMethod: String {
    case get = "GET"
    case post = "POST"
    case delete = "DELETE"
    case put = "PUT"
}

typealias CompletionHandler = ([String: String]?, NSError?) -> Void

class APIService {

    private let host = "https://api.github.com/"
    private let session = URLSession(configuration: .default)

    private var requestsRecord = Dictionary<NSNumber, Any>()
    static let shared = APIService()

    private init() {
        session.configuration.timeoutIntervalForRequest = 10
    }

    /// 获取数据
    /// - Parameters:
    ///   - method: GET ｜ POST ｜PUT｜DELETE
    ///   - handler: 请求完成后的回调
    func fetchData(method: HttpRequestMethod, completionHandler handler: @escaping CompletionHandler) {

        guard let url = URL(string: host) else {
            handler(
                nil,
                NSError(domain: "com.cgg.local",
                        code: -1,
                        userInfo: [NSLocalizedDescriptionKey: "host格式不正确"])
            )
            return
        }

        var request = URLRequest(url: url)
        request.httpMethod = method.rawValue

        let date = Date()
        var task: URLSessionDataTask!
        task = session.dataTask(with: request) { [weak self] (data, response, error) in
            self?.handleRequestResult(data: data, response: response, error: error, for: task)
        }
        addRequestRecord(task: task, handler: handler)
        task.resume()
        StoreService.shared.insertRequest(task, date: date)
    }

    private func handleRequestResult(data: Data?, response: URLResponse?, error: Error?, for task: URLSessionDataTask) -> Void {

        let date = Date()
        defer {
            removeRequestRecord(task: task)
            StoreService.shared.insertRequestResult(
                errorMsg: (error as NSError?)?.localizedDescription,
                data: data,
                date: date
            )
        }

        guard let handler = findRequestRecord(for: task) else {
            return
        }

        handleRequestResult(data: data, response: response, error: error, for: handler)
    }

    //should be private, open for testing now
    func handleRequestResult(data: Data?, response: URLResponse?, error: Error?, for handler: @escaping CompletionHandler) -> Void {

        // http error
        if let error = error as NSError? {
            DispatchQueue.main.async {
                handler(nil, error)
            }
            return
        }

        // empty data
        guard let data = data else {
            DispatchQueue.main.async {
                handler(nil, nil)
            }
            return
        }

        // parse error
        guard let model: [String: String] = Parser.parse(json: data) else {
            DispatchQueue.main.async {
                handler(
                    nil,
                    NSError(domain: "com.cgg.local",
                            code: -2,
                            userInfo: [NSLocalizedDescriptionKey: "解析json数据出错"])
                )
            }
            return
        }

        // succeed
        DispatchQueue.main.async {
            handler(model, nil)
        }
    }
}

// MARK: - RequestRecord Management
extension APIService {

    private func addRequestRecord(task: URLSessionDataTask, handler: @escaping CompletionHandler) {
        objc_sync_enter(requestsRecord)
        requestsRecord[NSNumber(value: task.taskIdentifier)] = handler
        objc_sync_exit(requestsRecord)
    }

    private func removeRequestRecord(task: URLSessionDataTask) {
        objc_sync_enter(requestsRecord)
        requestsRecord.removeValue(forKey: NSNumber(value: task.taskIdentifier))
        objc_sync_exit(requestsRecord)
    }

    private func findRequestRecord(for task: URLSessionDataTask) -> CompletionHandler? {
        objc_sync_enter(requestsRecord)
        let handler = requestsRecord[NSNumber(value: task.taskIdentifier)]
        objc_sync_exit(requestsRecord)

        return handler as? CompletionHandler
    }
}
